/*
 *  MainWindow.mm
 *  iActivator
 *
 *  Created by The Operator on 19/07/07.
 *  Copyright 2007 The Operator. All rights reserved.
 *
 * This software is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU General Public License version 2 for more details
 */
 
#import "MainWindow.h"
#include "PhoneInteraction/UtilityFunctions.h"
#include "PhoneInteraction/PhoneInteraction.h"

#include <unistd.h>

enum
{
	MENU_ITEM_GENERATE = 10,
	MENU_ITEM_PUT_PEM = 11,
	MENU_ITEM_ACTIVATE = 12,
	MENU_ITEM_DEACTIVATE = 13,
	MENU_ITEM_RETURN_TO_JAIL = 14,
	MENU_ITEM_JAILBREAK = 15,
	MENU_ITEM_BACKUP_ACTIVATION = 16
};


static MainWindow *g_mainWindow;


void updateStatus(const char *msg, bool waiting)
{

	if (g_mainWindow) {
		[g_mainWindow setStatus:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding] spinning:waiting];
	}

}

void phoneInteractionNotification(int type, const char *msg)
{

	if (g_mainWindow) {

		switch (type) {
			case NOTIFY_CONNECTED:
				[g_mainWindow setConnected:true];
				break;
			case NOTIFY_DISCONNECTED:
				[g_mainWindow setConnected:false];
				break;
			case NOTIFY_INITIALIZATION_FAILED:
			case NOTIFY_CONNECTION_FAILED:
				[g_mainWindow displayAlert:@"Failure" message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]];
				break;
			case NOTIFY_ACTIVATION_SUCCESS:
				[g_mainWindow setActivated:true];
				[g_mainWindow displayAlert:@"Success" message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]];
				break;
			case NOTIFY_DEACTIVATION_SUCCESS:
				[g_mainWindow setActivated:false];
				[g_mainWindow displayAlert:@"Success" message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]];
				break;
			case NOTIFY_JAILBREAK_SUCCESS:
				[g_mainWindow endDisplayWaitingSheet];
				[g_mainWindow setJailbroken:true];
                [g_mainWindow checkConnections];
				[g_mainWindow displayAlert:@"Success" message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]];
				break;
			case NOTIFY_ACTIVATION_FAILED:
			case NOTIFY_DEACTIVATION_FAILED:
			case NOTIFY_PUTPEM_FAILED:
			case NOTIFY_PUTSERVICES_FAILED:
			case NOTIFY_PUTFSTAB_FAILED:
			case NOTIFY_JAILBREAK_FAILED:
			case NOTIFY_JAILBREAK_FAIL_USER_COULDNT_HOLD:
				[g_mainWindow endDisplayWaitingSheet];
			case NOTIFY_GET_ACTIVATION_FAILED:
				[g_mainWindow updateStatus];
				[g_mainWindow displayAlert:@"Failure" message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]];
				break;
			case NOTIFY_PUTPEM_SUCCESS:
				[g_mainWindow endDisplayWaitingSheet];
			case NOTIFY_GET_ACTIVATION_SUCCESS:
				[g_mainWindow updateStatus];
				[g_mainWindow displayAlert:@"Success" message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]];
				break;
			case NOTIFY_JAILBREAK_RECOVERY_WAIT:
				[g_mainWindow startDisplayWaitingSheet:nil message:@"Please press and hold Home + Sleep on your iPhone until this dialog window disappears..." image:[NSImage imageNamed:@"home_sleep_buttons"] cancelButton:true runModal:true];
				break;
			case NOTIFY_JAILBREAK_RECOVERY_CONNECTED:
				[g_mainWindow endDisplayWaitingSheet];
				[g_mainWindow startDisplayWaitingSheet:nil message:@"Waiting for jail break..." image:[NSImage imageNamed:@"jailbreak"] cancelButton:false runModal:false];
				[g_mainWindow setRecoveryMode:true];
				break;
			case NOTIFY_JAILBREAK_RECOVERY_DISCONNECTED:
				[g_mainWindow setRecoveryMode:false];
				break;
			case NOTIFY_JAILBREAK_RESTORE_CONNECTED:
				[g_mainWindow setRestoreMode:true];
				break;
			case NOTIFY_JAILBREAK_RESTORE_DISCONNECTED:
				[g_mainWindow setRestoreMode:false];
				break;
			case NOTIFY_JAILBREAK_CANCEL:
				[g_mainWindow endDisplayWaitingSheet];
				[g_mainWindow updateStatus];
				break;
			case NOTIFY_CONNECTION_SUCCESS:
			case NOTIFY_INITIALIZATION_SUCCESS:
			case NOTIFY_PUTFSTAB_SUCCESS:
			case NOTIFY_PUTSERVICES_SUCCESS:
			default:
				break;
		}

	}

}

void utilityFunctionNotification(int type, const char *msg)
{
	
	if (g_mainWindow) {

		switch (type) {
			case NOTIFY_ACTIVATION_GEN_FAILED:
				[g_mainWindow displayAlert:@"Failure" message:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]];
				break;
			case NOTIFY_ACTIVATION_GEN_SUCCESS:
			default:
				break;
		}

	}

}

@implementation MainWindow

#pragma mark Table Data Source

- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
    return [m_packages count];
}

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
    NSArray *keys = [m_packages allKeys];

    NSString *packageId = [keys objectAtIndex:rowIndex];
    NSDictionary *pkg = [m_packages objectForKey:packageId];
    
    if ([[aTableColumn identifier] isEqualToString:@"label"]) {
        NSString *name = [pkg objectForKey:@"RDPxlPackageName"];
        NSString *version = [pkg objectForKey:@"RDPxlPackageVersion"];

        NSString *label = [NSString stringWithFormat:@"%@ %@", name ? name : packageId, version ? version : @""];
        return label;
    }
    else if ([[aTableColumn identifier] isEqualToString:@"icon"]) {
        NSImage *icon = [pkg objectForKey:@"RDPxlPackageIconData"];
        
        return icon;
    }
    
    return nil;
}

- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
{
    if ([packageList selectedRow] != -1) {
        [removeButton setEnabled:[self isConnected]];
    }
}

#pragma mark PXL File Handlers

- (void) application:(NSApplication *)app openFile:(NSString *)filename
{
    if ([[filename pathExtension] isEqualToString:@"pxl"]) {
        [self installPackage:filename];
    }
}

#pragma mark Other goo

- (void)dealloc
{

	if (m_phoneInteraction != NULL) {
		delete m_phoneInteraction;
	}

    [m_packages release];
	[super dealloc];
}

- (void)awakeFromNib
{
	[NSApp setDelegate:self];
    [packageList setDataSource:self];

	g_mainWindow = self;
	m_connected = false;
	m_recoveryMode = false;
	m_restoreMode = false;
	m_jailbroken = false;
	m_activated = false;
	m_statusSpinning = false;
	m_performingJailbreak = false;
	m_returningToJail = false;
	m_phoneInteraction = NULL;
	m_modal = false;
    m_pxlInstalled = false;
    m_packages = [[NSMutableDictionary alloc] init];
	m_phoneInteraction = PhoneInteraction::getInstance(updateStatus, phoneInteractionNotification);
}

- (void)displayAlert:(NSString*)title message:(NSString*)msg
{
	NSRunAlertPanel(title, msg, @"OK", nil, nil, nil);
}

- (void)startDisplayWaitingSheet:(NSString*)title message:(NSString*)msg image:(NSImage*)img
					cancelButton:(bool)cancel runModal:(bool)modal
{

	if (title != nil) {
		[waitDialog setTitle:title];
	}

	if (msg != nil) {
		[waitDialogMessage setStringValue:msg];
	}

	if (img != nil) {
		[waitDialogImage setImage:img];
	}

	if (cancel) {
		[waitDialogCancel setHidden:NO];
	}
	else {
		[waitDialogCancel setHidden:YES];
	}

	[waitDialogSpinner startAnimation:self];

	[NSApp beginSheet:waitDialog modalForWindow:self modalDelegate:nil
	   didEndSelector:nil contextInfo:nil];

	m_modal = modal;

	if (modal) {
		[NSApp runModalForWindow:waitDialog];
	}

}

- (void)endDisplayWaitingSheet
{
	[waitDialogSpinner stopAnimation:self];
	[NSApp endSheet:waitDialog];
	[waitDialog orderOut:self];

	if (m_modal) {
		[NSApp stopModal];
	}

}

- (void)setStatus:(NSString*)text spinning:(bool)spin
{
	[statusBar setStringValue:text];

	if (m_statusSpinning != spin) {
		m_statusSpinning = spin;

		if (spin) {
			[statusSpinner startAnimation:self];
		}
		else {
			[statusSpinner stopAnimation:self];
		}

	}

}

- (void)removePackage:(NSString *)packageId
{
    if ([m_packages objectForKey:packageId] && [self isConnected]) {
        NSString *pxlPath;
    
        if ([self isJailbroken]) {
            pxlPath = @"/private/var/root/Media/PXL";
        }
        else {
            pxlPath = @"/PXL";
        }

        NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
        NSString *dropoff = [NSString stringWithFormat:@"%@/Dropoff/PxlPickup", pxlPath];
        
        NSDictionary *command = [NSDictionary dictionaryWithObjectsAndKeys:packageId,@"package",@"remove",@"command",nil];
        NSArray *array = [NSArray arrayWithObject:command];
        [myDict setObject:array forKey:@"commands"];
        
        NSString *plistFile = [NSString stringWithFormat:@"%@/pxlcommand.plist", NSTemporaryDirectory()];
        [myDict writeToFile:plistFile atomically:NO];        
        m_phoneInteraction->putFile([plistFile cString],(char *)[dropoff cString]);
        
        [[NSFileManager defaultManager] removeFileAtPath:plistFile handler:nil];
        while (m_phoneInteraction->existsFile([dropoff cString])) {
            sleep(2);
        }
        [self fillInstalled];

    }
}

- (void)installPackage:(NSString *)packageFile
{
    if ([self isConnected]) {
        NSString *pxlPath;
    
        NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];

        if ([self isJailbroken]) {
            pxlPath = @"/private/var/root/Media/PXL";
        }
        else {
            pxlPath = @"/PXL";
        }
        
        NSString *filename = [[packageFile pathComponents] lastObject];
        NSString *dropoff = [NSString stringWithFormat:@"%@/Dropoff/PxlPickup", pxlPath];
        
        NSDictionary *command = [NSDictionary dictionaryWithObjectsAndKeys:filename,@"package",@"install",@"command",nil];
        NSArray *array = [NSArray arrayWithObject:command];
        [myDict setObject:array forKey:@"commands"];
        
        NSString *plistFile = [NSString stringWithFormat:@"%@/pxlcommand.plist", NSTemporaryDirectory()];
        [myDict writeToFile:plistFile atomically:NO];        
        m_phoneInteraction->putFile((char *)[packageFile cString],(char *)[[NSString stringWithFormat:@"%@/Dropoff/%@", pxlPath, filename] cString]);
        m_phoneInteraction->putFile((char *)[plistFile cString],(char *)[dropoff cString]);
        [[NSFileManager defaultManager] removeFileAtPath:plistFile handler:nil];

        while (m_phoneInteraction->existsFile([dropoff cString])) {
            sleep(2);
        }
        
        [self fillInstalled];
    }
}

- (void)updateStatus
{
	NSString *statusStr = [NSString string];
	bool waiting = false;

	if ([self isInRestoreMode]) {
		statusStr = [statusStr stringByAppendingString:@"Connected to iPhone in restore mode"];
	}
	else if ([self isInRecoveryMode]) {
		statusStr = [statusStr stringByAppendingString:@"Connected to iPhone in recovery mode"];
	}
	else if ([self isConnected]) {
		statusStr = [statusStr stringByAppendingString:@"Connected to iPhone"];

        if (m_pxlInstalled) {
            statusStr = [statusStr stringByAppendingString:@" with PXL installed"];
        }

		if ([self isJailbroken]) {
			statusStr = [statusStr stringByAppendingString:@" (jailbroken)"];
		}

	}
	else {
		statusStr = [statusStr stringByAppendingString:@"Disconnected: waiting for iPhone..."];
		waiting = true;
	}

	[self setStatus:statusStr spinning:waiting];
}

- (void)resetSheet
{
    [self startDisplayWaitingSheet:nil
                           message:@"Please press and hold the Home + Sleep buttons for 3 seconds, then power off your phone, then press Sleep again to restart it."
                             image:[NSImage imageNamed:@"home_sleep_buttons"] cancelButton:true runModal:true];            
    
}

- (void) 

- (void) retrieveFirmwareVersions
{
    NSURL *url = [NSURL URLWithString:@"http://phobos.apple.com/versions"];
    NSDictionary *firmwareVersions = [NSDictionary dictionaryWithContentsOfURL:url];
    
    if (firmwareVersions) {
        NSDictionary *tempDict = [firmwareVersions objectForKey:@"MobileDeviceSoftwareVersions"];
        NSString *firmwareBuild = (NSString *)m_phoneInteraction->getValue((CFStringRef)@"BuildVersion");
        
        if (!tempDict)
            return;
        
        tempDict = [tempDict objectForKey:@"iPhone1,1"];
        if (!tempDict)
            return;
            
        tempDict = [tempDict objectForKey:firmwareBuild];
        if (!tempDict)
            return;
            
        tempDict = [tempDict objectForKey:@"Restore"];
        if (!tempDict)
            return;
            
        NSString *firmwareDisplay = [tempDict objectForKey:@"ProductVersion"];
        NSLog([NSString stringWithFormat:@"%@ -> %@", firmwareBuild, firmwareDisplay]);
        
        NSString *firmwareRestore = [tempDict objectForKey:@"FirmwareURL"];
        
        NSString *firmwareFile = [firmwareRestore lastPathComponent];
        NSString *firmwareCache = [[NSString stringWithFormat:@"~/Library/iTunes/iPhone Software Updates/%@", firmwareFile] stringByExpandingTildeInPath];
        
        if ([[NSFileManager defaultManager] fileExistsAtPath:firmwareCache]) {
        
        }
        else {
            
        }
    }

    
}

- (void)checkConnections
{
    if (m_connected) {
        [self endDisplayWaitingSheet];
        [self retrieveFirmwareVersions];
        if (m_phoneInteraction->existsFile(m_jailbroken ? "/private/var/root/Media/PXL" : "/PXL")) {
            m_pxlInstalled = true;
            [self fillInstalled];
            [removeButton setEnabled:YES];
            [packageList setEnabled:YES];
        }
        else {
            if (m_jailbroken) {
                if (m_pxlNeedsInstall) {
                    NSString *service = [[NSBundle mainBundle] pathForResource:@"net.riverdark.iphone.PXLdaemon" ofType:@"plist"];
                    NSString *daemon = [[NSBundle mainBundle] pathForResource:@"PXLdaemon" ofType:@""];
                    NSString *dylib = [[NSBundle mainBundle] pathForResource:@"armfp" ofType:@"dat"];
                    
                    m_phoneInteraction->putFile([dylib cString],"/usr/lib/libarmfp.dylib");
                    m_phoneInteraction->putFile([daemon cString],"/usr/sbin/PXLdaemon");
                    m_phoneInteraction->putFile([service cString],"/System/Library/LaunchDaemons/net.riverdark.iphone.PXLdaemon.plist");
                    
                    m_pxlNeedsInstall = false;
                    [self performSelector:@selector(resetSheet) withObject:self afterDelay:0.01];
                }
                else {
                    [self performSelector:@selector(returnToJail:) withObject:self afterDelay:0.01];
                }
            }
            else {
                m_pxlNeedsInstall = true;
                [self performSelector:@selector(performJailbreak:) withObject:self afterDelay:0.01];
            }
        }    
    }
}

- (void)setConnected:(bool)connected
{
	m_connected = connected;

    [removeButton setEnabled:NO];
    [packageList setEnabled:NO];
    [m_packages removeAllObjects];
    [packageList reloadData];

	if (m_connected) {
		[self setActivated:m_phoneInteraction->isPhoneActivated()];
		[self setJailbroken:m_phoneInteraction->isPhoneJailbroken()];
        [self checkConnections];

		if (m_returningToJail) {
			m_returningToJail = false;
			[self endDisplayWaitingSheet];
			[self displayAlert:@"Success" message:@"Successfully returned to jail."];
		}
	}
	else {
		[self setActivated:false];
		[self setJailbroken:false];
	}

	[self updateStatus];
}

- (bool)isConnected
{
	return m_connected;
}

- (void)setRecoveryMode:(bool)inRecovery
{
	m_recoveryMode = inRecovery;
	[self updateStatus];
}

- (bool)isInRecoveryMode
{
	return m_recoveryMode;
}

- (void)setRestoreMode:(bool)inRestore
{
	m_restoreMode = inRestore;
	[self updateStatus];
}

- (bool)isInRestoreMode
{
	return m_restoreMode;
}

- (void)setJailbroken:(bool)jailbroken
{
	m_jailbroken = jailbroken;

	if (m_jailbroken) {
		[putPEMButton setEnabled:YES];
		[returnToJailButton setEnabled:YES];
		[jailbreakButton setEnabled:NO];

		if ([self isActivated]) {
			[backupButton setEnabled:YES];
		}
		else {
			[backupButton setEnabled:NO];
		}

	}
	else {
		[putPEMButton setEnabled:NO];
		[returnToJailButton setEnabled:NO];
		[backupButton setEnabled:NO];

		if ([self isConnected]) {
			[jailbreakButton setEnabled:YES];
		}
		else {
			[jailbreakButton setEnabled:NO];
		}

	}

	[self updateStatus];
}

- (bool)isJailbroken
{
	return m_jailbroken;
}

- (void)setActivated:(bool)activated
{
	m_activated = activated;

	if (m_activated) {
		[activateButton setEnabled:NO];
		[deactivateButton setEnabled:YES];

		if ([self isJailbroken]) {
			[backupButton setEnabled:YES];
		}
		else {
			[backupButton setEnabled:NO];
		}

	}
	else {

		if (![self isConnected]) {
			[activateButton setEnabled:NO];
		}
		else {
			[activateButton setEnabled:YES];
		}

		[backupButton setEnabled:NO];
		[deactivateButton setEnabled:NO];
	}

	[self updateStatus];
}

- (bool)isActivated
{
	return m_activated;
}

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApp
{
	return YES;
}

- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
{

	switch ([menuItem tag]) {
		case MENU_ITEM_ACTIVATE:

			if (![self isConnected] || [self isActivated]) {
				return NO;
			}
			
			break;
		case MENU_ITEM_DEACTIVATE:

			if (![self isConnected] || ![self isActivated]) {
				return NO;
			}

			break;
		case MENU_ITEM_JAILBREAK:

			if (![self isConnected] || [self isJailbroken]) {
				return NO;
			}

			break;
		case MENU_ITEM_RETURN_TO_JAIL:

			if (![self isConnected] || ![self isJailbroken]) {
				return NO;
			}

			break;
		case MENU_ITEM_PUT_PEM:

			if (![self isConnected] || ![self isJailbroken]) {
				return NO;
			}

			break;
		case MENU_ITEM_GENERATE:
		default:
			break;
	}

	return YES;
}

- (IBAction)generateActivation:(id)sender
{
	[self setStatus:@"Generating activation..." spinning:true];

	int retval;
	const char *imeiVal;
	const char *iccidVal;
	char deviceid[41];

	// get the IMEI and ICCID
	[NSApp beginSheet:imeiDialog modalForWindow:self modalDelegate:nil
	   didEndSelector:nil contextInfo:nil];

	while (1) {
		retval = [NSApp runModalForWindow:imeiDialog];

		if (retval == -1) {
			[NSApp endSheet:imeiDialog];
			[imeiDialog orderOut:self];
			[self updateStatus];
			return;
		}

		imeiVal = [[imeiTextField stringValue] UTF8String];

		if (!UtilityFunctions::validateIMEI(imeiVal)) {
			[self displayAlert:@"Error" message:@"You entered an invalid IMEI value.  Try again."];
			continue;
		}

		iccidVal = [[iccidTextField stringValue] UTF8String];
	
		if (!UtilityFunctions::validateICCID(iccidVal)) {
			[self displayAlert:@"Error" message:@"You entered an invalid ICCID value.  Try again."];
			continue;
		}

		if (!UtilityFunctions::findDeviceID(deviceid)) {
			[self displayAlert:@"Error" message:@"Can't find device ID.  Check to ensure that ~/Library/Lockdown/ exists and contains a .plist file."];
			[NSApp endSheet:imeiDialog];
			[imeiDialog orderOut:self];
			[self updateStatus];
			return;
		}

		break;
	}

	[NSApp endSheet:imeiDialog];
	[imeiDialog orderOut:self];

	// now find the PEM file
	NSString *pemfile = [[NSBundle mainBundle] pathForResource:@"iPhoneActivation_private" ofType:@"pem"];
	
	if (pemfile == nil) {
		[self displayAlert:@"Error" message:@"Error finding PEM file to sign activation with."];
		[self updateStatus];
		return;
	}

	CFDictionaryRef activationrequest;

	if (!UtilityFunctions::generateActivationRequest_All(&activationrequest, [pemfile UTF8String],
														 deviceid, imeiVal, iccidVal,
														 utilityFunctionNotification)) {
		[self displayAlert:@"Error" message:@"Error generating the activation file."];
		[self updateStatus];
		return;
	}

	NSSavePanel *plistSaver = [NSSavePanel savePanel];
	[plistSaver setTitle:@"Choose a Location for the activation file"];
	[plistSaver setRequiredFileType:@"plist"];

	retval = [plistSaver runModal];

	if (retval == NSFileHandlingPanelOKButton) {
		[(NSDictionary*)activationrequest writeToFile:[plistSaver filename] atomically:YES];
	}

	CFRelease(activationrequest);
	[self updateStatus];
}

- (IBAction)imeiDialogCancel:(id)sender
{
	[NSApp stopModalWithCode:-1];
}

- (IBAction)imeiDialogOk:(id)sender
{
	[NSApp stopModalWithCode:0];
}

- (IBAction)putPEMOnPhone:(id)sender
{
	[self setStatus:@"Putting PEM file on phone..." spinning:true];
	
	NSString *pemfile = [[NSBundle mainBundle] pathForResource:@"iPhoneActivation" ofType:@"pem"];

	if (pemfile == nil) {
		[self displayAlert:@"Error" message:@"Error finding PEM file to put on phone."];
		[self updateStatus];
		return;
	}

	m_phoneInteraction->putPEMOnPhone([pemfile UTF8String]);
}

- (IBAction)returnToJail:(id)sender
{
	[self setStatus:@"Returning to jail..." spinning:true];

	NSString *servicesFile = [[NSBundle mainBundle] pathForResource:@"Services" ofType:@"plist"];

	if (servicesFile == nil) {
		[self displayAlert:@"Error" message:@"Error finding Services.plist file."];
		[self updateStatus];
		return;
	}

	NSString *fstabFile = [[NSBundle mainBundle] pathForResource:@"fstab" ofType:@""];

	if (fstabFile == nil) {
		[self displayAlert:@"Error" message:@"Error finding fstab file."];
		[self updateStatus];
		return;
	}
	
	if (!m_phoneInteraction->putServicesFileOnPhone([servicesFile UTF8String])) {
		[self displayAlert:@"Error" message:@"Error writing Services.plist file to phone."];
		[self updateStatus];
		return;
	}

	if (!m_phoneInteraction->putFstabFileOnPhone([fstabFile UTF8String])) {
		[self displayAlert:@"Error" message:@"Error writing fstab file to phone."];
		[self updateStatus];
		return;
	}

	m_returningToJail = true;

	[self startDisplayWaitingSheet:nil
						   message:@"Please press and hold the Home + Sleep buttons for 3 seconds, then power off your phone, then press Sleep again to restart it."
							 image:[NSImage imageNamed:@"home_sleep_buttons"] cancelButton:true runModal:true];
}

- (void) fillInstalled
{
    NSString *pxlDaemonPath;
    
    if (m_phoneInteraction->isPhoneJailbroken()) {
        pxlDaemonPath = @"/private/var/root/Media/PXL/DB";
    }
    else {
        pxlDaemonPath = @"/PXL/DB";
    }
    
    [m_packages removeAllObjects];
    
    NSMutableArray *array = [[NSMutableArray alloc] init];
    m_phoneInteraction->contentsOfDirectory([pxlDaemonPath cString],(CFMutableArrayRef)array);
    
    if ([array count]) {
        NSEnumerator *arrayEnum = [array objectEnumerator];
        id walk;
        while (walk = [arrayEnum nextObject]) {
            NSString *filename = (NSString *)walk;
            
            if ([[filename pathExtension] isEqualToString:@"plist"]) {
                NSString *tempfileSrc, *tempfileDest, *tempiconSrc, *tempiconDest;
                NSString *package = [filename stringByDeletingPathExtension];
                
                tempfileSrc = [NSString stringWithFormat:@"%@/%@", pxlDaemonPath, filename];
                tempfileDest = [NSString stringWithFormat:@"%@/%@", NSTemporaryDirectory(), filename];                
                tempiconSrc = [NSString stringWithFormat:@"%@/%@", pxlDaemonPath, [package stringByAppendingPathExtension:@"png"]];
                tempiconDest = [NSString stringWithFormat:@"%@/%@", NSTemporaryDirectory(), [package stringByAppendingPathExtension:@"png"]];
                
                if (m_phoneInteraction->getFile([tempfileSrc cString], [tempfileDest cString])) {
                    NSMutableDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:tempfileDest] mutableCopy];
                    if (dict) {
                        if (m_phoneInteraction->existsFile([tempiconSrc cString])) {
                            if (m_phoneInteraction->getFile([tempiconSrc cString], [tempiconDest cString])) {
                                NSImage *packageIcon;
                                
                                packageIcon = [[[NSImage alloc] initWithContentsOfFile:tempiconDest] autorelease];
                                [dict setObject:packageIcon forKey:@"RDPxlPackageIconData"];
                                [[NSFileManager defaultManager] removeFileAtPath:tempiconDest handler:nil];
                            }
                        }
                        [[NSFileManager defaultManager] removeFileAtPath:tempfileDest handler:nil];
                        [m_packages setObject:[dict autorelease] forKey:package];
                    }
                }
            }
        }
    }
    [array release];
    [packageList reloadData];
}

- (IBAction)activate:(id)sender
{
	NSOpenPanel *plistOpener = [NSOpenPanel openPanel];
	[plistOpener setTitle:@"Select an Activation plist File"];
	[plistOpener setCanChooseDirectories:NO];
	[plistOpener setAllowsMultipleSelection:NO];

	NSArray *fileTypes = [NSArray arrayWithObject:@"plist"];
	int retval = [plistOpener runModalForTypes:fileTypes];
	
	if (retval != NSOKButton) {
		return;
	}
	
	m_phoneInteraction->activate([[plistOpener filename] UTF8String]);
}

- (IBAction)deactivate:(id)sender
{
	m_phoneInteraction->deactivate();
}

- (IBAction)performJailbreak:(id)sender
{
	NSString *firmwarePath;

	// first things first -- get the path to the unzipped firmware files
	NSOpenPanel *firmwareOpener = [NSOpenPanel openPanel];
	[firmwareOpener setTitle:@"Select where you unzipped the firmware files"];
	[firmwareOpener setCanChooseDirectories:YES];
	[firmwareOpener setCanChooseFiles:NO];
	[firmwareOpener setAllowsMultipleSelection:NO];
	
	while (1) {
		
		if ([firmwareOpener runModalForTypes:nil] != NSOKButton) {
			return;
		}
		
		firmwarePath = [firmwareOpener filename];

		if ([[NSFileManager defaultManager] fileExistsAtPath:[firmwarePath stringByAppendingString:@"/Restore.plist"]]) {
			break;
		}
		
		[self displayAlert:@"Error" message:@"Specified path does not contain firmware files.  Try again."];
		return;
	}

	NSString *servicesFile = [[NSBundle mainBundle] pathForResource:@"Services_mod" ofType:@"plist"];
	
	if (servicesFile == nil) {
		[self displayAlert:@"Error" message:@"Error finding modified Services.plist file."];
		return;
	}
	
	NSString *fstabFile = [[NSBundle mainBundle] pathForResource:@"fstab_mod" ofType:@""];

	if (fstabFile == nil) {
		[self displayAlert:@"Error" message:@"Error finding modified fstab file."];
		return;
	}

	m_performingJailbreak = true;
	m_phoneInteraction->performJailbreak([firmwarePath UTF8String], [fstabFile UTF8String],
										 [servicesFile UTF8String]);
}

- (IBAction)removePackageButton:(id) sender
{
    int row = [packageList selectedRow];
    
    if (row != -1) {
        NSString *packageId = [[m_packages allKeys] objectAtIndex:row];
        
        if (packageId)
            [self removePackage:packageId];
    }
    else {
        NSBeep();
    }
}

- (IBAction)waitDialogCancel:(id)sender
{

	if (m_performingJailbreak) {
		m_performingJailbreak = false;
		m_phoneInteraction->cancelJailbreak();
	}
	else if (m_returningToJail) {
		m_returningToJail = false;
	}

}

- (void) backupActivation:(id) sender
{

}


- (IBAction)showAboutPanel:(id)sender
{
	[aboutBox makeKeyAndOrderFront:sender];
}

@end
